/*
 * @Author: 13177709051 3506463075@qq.com
 * @Date: 2024-11-08 16:57:05
 * @LastEditors: 13177709051 3506463075@qq.com
 * @LastEditTime: 2024-11-12 11:29:49
 * @FilePath: /video-on-demand-project/video-on-demand-source/util.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
/*
 * @Author: 13177709051 3506463075@qq.com
 * @Date: 2024-11-08 16:57:05
 * @LastEditors: 13177709051 3506463075@qq.com
 * @LastEditTime: 2024-11-11 14:43:26
 * @FilePath: /video-on-demand-project/video-on-demand-source/util.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */

// 当头文件首次被包含时，__MY_UTIL__ 并未定义，#ifndef 条件成立，因此进入 #define __MY_UTIL__ 和之后的代码。
// 当文件再次被包含时，__MY_UTIL__ 已经被定义，#ifndef 条件不成立，头文件的内容会被跳过，避免重复定义。
// 这种写法确保了 my_util.h 只会在每个编译单元中被包含一次，即使在多个源文件中都包含了这个头文件。
#ifndef __MY_UTIL__
#define __MY_UTIL__

#include <iostream>
#include <fstream>
#include <sstream>
#include <memory>
#include <string>
#include <unistd.h>
#include <sys/stat.h>
#include <jsoncpp/json/json.h>

// 服务端工具类实现 - 文件实用工具类设计
// 在视频点播系统中因为涉及到文件上传, 需要对上传的文件进行备份, 因此首先设计封装文件操作类, 这个类封装完毕之后, 则在任意模块中对文件进行操作时都将变得简单化
//  1. 获取文件大小 (属性)
//  2. 判断文件是否存在
//  3. 向文件写入数据
//  4. 从文件读取数据
//  5. 针对目录文件多一个创建目录

namespace aod
{
    class FileUtil
    {
    private:
        std::string _name; // 文件路径名称
    public:
        FileUtil(const std::string name) : _name(name) {}
        bool Exists()
        {
            // access的F_OK专门用于检测文件是否存在--- 存在则返回0
            int ret = access(_name.c_str(), F_OK);
            if (ret != 0)
            {
                std::cout << "file is not exists\n";
                return false;
            }
            return true;
        } // 判断文件是否存在
        size_t Size()
        {
            if (this->Exists() == false)
            {
                return 0;
            }
            struct stat st;
            // stat接口用于获取文件属性，其中 st_size就是文件大小成员
            int ret = stat(_name.c_str(), &st);
            if (ret != 0)
            {
                std::cout << "get file stat failed!\n";
                return 0;
            }
            return st.st_size;
        } // 获取文件大小
        bool GetContent(std::string *body)
        {
            std::ifstream ifs;
            ifs.open(_name, std::ios::binary);
            if (ifs.is_open() == false)
            {
                std::cout << "open file failed!\n";
                return false;
            }
            size_t flen = this->Size();
            body->resize(flen);
            ifs.read(&(*body)[0], flen);
            if (ifs.good() == false)
            {
                std::cout << "read file content failed!\n";
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        } // 读取文件数据到body中
        bool SetContent(const std::string &body)
        {
            std::ofstream ofs;
            ofs.open(_name, std::ios::binary);
            if (ofs.is_open() == false)
            {
                std::cout << "open file failed!\n";
                return false;
            }
            ofs.write(body.c_str(), body.size());
            if (ofs.good() == false)
            {
                std::cout << "write file content failed!\n";
                ofs.close();
                return false;
            }
            ofs.close();
            return true;
        } // 向文件写入数据
        bool CreateDirectory()
        {
            if (this->Exists())
            {
                return true;
            }
            mkdir(_name.c_str(), 0777);
            return true;
        } // 针对目录时创建目录
    };
    class JsonUtil
    {
    public:
        static bool Serialize(const Json::Value &value, std::string *body)
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());

            std::stringstream ss;
            int ret = sw->write(value, &ss);
            if (ret != 0)
            {
                std::cout << "Serialize failed!\n";
                return false;
            }
            *body = ss.str();
            return true;
        }
        static bool UnSerialize(const std::string &body, Json::Value *value)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());

            std::string err;
            bool ret = cr->parse(body.c_str(), body.c_str() + body.size(), value, &err);
            if (ret == false)
            {
                std::cout << "UnSerialize failed!\n";
                return false;
            }
            return true;
        }
    };
}

#endif

// namespace aod
// {
//     class FileUtil
//     {
//     private:
//         std::string _name; // 文件路径名称
//     public:
//         FileUtil(const std::string name) : _name(name) {}

//         // 判断文件是否存在
//         bool Exists()
//         {
//             // access的 F_OK 专门用于检测文件是否存在 --- 存在则返回0
//             int ret = access(_name.c_str(), F_OK);
//             if (ret != 0)
//             {
//                 std::cout << "file is not exists\n";
//                 return false;
//             }
//             return true;
//         }

//         // 获取文件大小
//         size_t Size()
//         {
//             if (this->Exists() == false)
//             {
//                 return 0;
//             }
//             struct stat st;
//             // stat 接口用于获取文件属性, 其中 st_size 就是文件大小成员
//             int ret = stat(_name.c_str(), &st);
//             if (ret != 0)
//             {
//                 std::cout << "get file stat failed!\n";
//                 return 0;
//             }
//             return st.st_size;
//         }

//         // 读取文件数据到 body 中
//         bool GetContent(std::string *body)
//         {
//             std::ifstream ifs;
//             ifs.open(_name, std::ios::binary);
//             if (ifs.is_open() == false)
//             {
//                 std::cout << "open file failed\n";
//                 return false;
//             }
//             size_t flen = this->Size();
//             body->resize(flen);
//             ifs.read(&(*body)[0], flen);
//             if (ifs.good() == false)
//             {
//                 std::cout << "read file content failed!\n";
//                 ifs.close();
//                 return false;
//             }
//             ifs.close();
//             return true;
//         }

//         // 向文件写入数据
//         bool SetContent(const std::string &body)
//         {
//             std::ofstream ofs;
//             ofs.open(_name, std::ios::binary);
//             if (ofs.is_open() == false)
//             {
//                 std::cout << "open file failed!\n";
//                 return false;
//             }
//             ofs.write(body.c_str(), body.size());
//             if (ofs.good() == false)
//             {
//                 std::cout << "write file content failed!\n";
//                 ofs.close();
//                 return false;
//             }
//             ofs.close();
//             return true;
//         }

//         // 针对目录创建目录
//         bool CreateDirectory()
//         {
//             if (this->Exists())
//             {
//                 return true;
//             }
//             mkdir(_name.c_str(), 0777);
//             return true;
//         }
//     };
//     class JsonUtil
//     {
//     public:
//         static bool Serialize(const Json::Value &value, std::string *body)
//         {
//             Json::StreamWriterBuilder swb;
//             std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());

//             std::stringstream ss;
//             int ret = sw->write(value, &ss);
//             if (ret != 0)
//             {
//                 std::cout << "Serialize failed!\n";
//                 return false;
//             }
//             *body = ss.str();
//             return true;
//         }
//         static bool UnSerialize(const std::string &body, Json::Value *value)
//         {
//             Json::CharReaderBuilder crb;
//             std::unique_ptr<Json::CharReader> cr(crb.newCharReader());

//             std::string err;
//             bool ret = cr->parse(body.c_str(), body.c_str() + body.size(), value, &err);
//             if (ret == false)
//             {
//                 std::cout << "UnSerialize failed!\n";
//                 return false;
//             }
//             return true;
//         }
//     };
// }
// #endif

// 问题一 : 为什么需要使用 c_str()
//  std::string 是 C++ 标准库提供的字符串类，它提供了更高层次的封装，支持动态大小、内存管理、字符串操作等特性，且它不是 C 风格的字符串（即不是以 '\0' 结尾的字符数组）。
//  c_str() 是 std::string 提供的一个方法，用于返回指向 C 风格字符串（const char*）的指针。调用 c_str() 时，std::string 对象中的字符会被转换成 C 风格字符串的形式，并以 '\0' 结尾。这是因为 C 风格的字符串本质上是一个字符数组（char[]），并且它以空字符 '\0' 作为结束标志。
//  std::string 本身不能直接传递给需要 C 风格字符串的函数，如 access 和 stat，因为它们要求的是 const char* 类型。
//  通过调用 c_str() 方法，std::string 会返回一个 C 风格字符串（const char*），可以与这些底层的 C 函数兼容。
//  这也是为什么在 FileUtil 类中使用 c_str() 方法的原因：为了将 std::string 转换成适合传递给系统函数的 C 风格字符串。

// 问题二 : 什么时候使用 * , 什么时候使用 const &
// 只读 使用 const &, 读写使用 *, 只写 &

// 问题三 : 为什么使用二进制方式打开
//  二进制数据文件，图像、音频文件、压缩文件等必须使用二进制模式，以保证文件内容的原始性。